﻿using System;
using System.Globalization;
using System.Runtime.InteropServices;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// Defines a rectangle.
    /// </summary>
    [Serializable]
	[StructLayout(LayoutKind.Sequential)]
    public struct Rectangle : IEquatable<Rectangle>
    {
        #region Public Fields
        /// <summary>
        /// Specifies the x-coordinate of the rectangle.
        /// </summary>
        public int X;
        /// <summary>
        /// Specifies the y-coordinate of the rectangle.
        /// </summary>
        public int Y;
        /// <summary>
        /// Specifies the width of the rectangle.
        /// </summary>
        public int Width;
        /// <summary>
        /// Specifies the height of the rectangle.
        /// </summary>
        public int Height;
        #endregion

        #region Public Properties
        /// <summary>
        /// Returns the y-coordinate of the bottom of the rectangle.
        /// </summary>
        public int Bottom
        {
            get { return (this.Y + this.Height); }
        }
        /// <summary>
        /// Gets the Point that specifies the center of the rectangle.
        /// </summary>
        public Point Center
        {
            get { return new Point((this.Right - (this.Width >> 1)), (this.Bottom - (this.Height >> 1))); }
        }
        /// <summary>
        /// Returns a Rectangle with all of its values set to zero.
        /// </summary>
        public static Rectangle Empty
        {
            get { return new Rectangle(); }
        }
        /// <summary>
        /// Gets a value that indicates whether the Rectangle is empty.
        /// </summary>
        public bool IsEmpty
        {
            get { return ((X == 0) && (Y == 0) && (Width == 0) && (Height == 0)); }
        }
        /// <summary>
        /// Returns the x-coordinate of the left side of the rectangle.
        /// </summary>
        public int Left
        {
            get { return this.X; }
        }
        /// <summary>
        /// Gets or sets the upper-left value of the Rectangle.
        /// </summary>
        public Point Location
        {
            get
            {
                return new Point(X, Y);
            }
            set
            {
                X = value.X;
                Y = value.Y;
            }
        }
        /// <summary>
        /// Returns the x-coordinate of the right side of the rectangle.
        /// </summary>
        public int Right
        {
            get { return (this.X + this.Width); }
        }
        /// <summary>
        /// Returns the y-coordinate of the top of the rectangle.
        /// </summary>
        public int Top
        {
            get { return this.Y; }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of Rectangle.
        /// </summary>
        /// <param name="x">The x-coordinate of the rectangle.</param>
        /// <param name="y">The y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        public Rectangle(int x, int y, int width, int height)
        {
            this.X = x;
            this.Y = y;
            this.Width = width;
            this.Height = height;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Determines whether this Rectangle entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The rectangle to evaluate.</param>
        /// <param name="result">On exit, is true if this Rectangle entirely contains the specified Rectangle, or false if not.</param>
        public void Contains(ref Rectangle value, out bool result)
        {
            result = (this.Left <= value.Left && this.Right >= value.Right &&
                      this.Top <= value.Top && this.Bottom >= value.Bottom);
        }
        /// <summary>
        /// Determines whether this Rectangle entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The rectangle to evaluate.</param>
        public bool Contains(Rectangle value)
        {
            return (this.Left <= value.Left && this.Right >= value.Right &&
                    this.Top <= value.Top && this.Bottom >= value.Bottom);
        }
        /// <summary>
        /// Determines whether this Rectangle contains a specified Point.
        /// </summary>
        /// <param name="value">The point to evaluate.</param>
        /// <param name="result">true if the specified Point is contained within this Rectangle; false otherwise.</param>
        public void Contains(ref Point value, out bool result)
        {
            result = (this.Left <= value.X && this.Right >= value.X &&
                      this.Top <= value.Y && this.Bottom >= value.Y);
        }
        /// <summary>
        /// Determines whether this Rectangle contains a specified Point.
        /// </summary>
        /// <param name="value">The point to evaluate.</param>
        public bool Contains(Point value)
        {
            return (this.Left <= value.X && this.Right >= value.X &&
                    this.Top <= value.Y && this.Bottom >= value.Y);
        }
        /// <summary>
        /// Determines whether this Rectangle contains a specified point represented by its x- and y-coordinates.
        /// </summary>
        /// <param name="x">The x-coordinate of the specified point.</param>
        /// <param name="y">The y-coordinate of the specified point.</param>
        public bool Contains(int x, int y)
        {
            return (this.Left <= x && this.Right >= x &&
                    this.Top <= y && this.Bottom >= y);
        }
        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">Object to make the compraison with.</param>
        public override bool Equals(object obj)
        {
            return (obj is Rectangle) ? this == ((Rectangle)obj) : false;
        }
        /// <summary>
        /// Determines whether the specified Object is equal to the Rectangle.
        /// </summary>
        /// <param name="other">The Object to compare with the current Rectangle.</param>
        public bool Equals(Rectangle other)
        {
            return this == other;
        }
        /// <summary>
        /// Gets the hash code for this object.
        /// </summary>
        public override int GetHashCode()
        {
            return (this.X ^ this.Y ^ this.Width ^ this.Height);
        }
        /// <summary>
        /// Pushes the edges of the Rectangle out by the horizontal and vertical values specified.
        /// </summary>
        /// <param name="horizontalValue">Value to push the sides out by.</param>
        /// <param name="verticalValue">Value to push the top and bottom out by.</param>
        public void Inflate(int horizontalValue, int verticalValue)
        {
            X -= horizontalValue;
            Y -= verticalValue;
            Width += horizontalValue * 2;
            Height += verticalValue * 2;
        }
        /// <summary>
        /// Determines whether a specified Rectangle intersects with this Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <param name="result">true if the specified Rectangle intersects with this one; false otherwise.</param>
        public void Intersects(ref Rectangle value, out bool result)
        {
            result = !(value.Left > Right
                     || value.Right < Left
                     || value.Top > Bottom
                     || value.Bottom < Top
                    );

        }
        /// <summary>
        /// Determines whether a specified Rectangle intersects with this Rectangle.
        /// </summary>
        /// <param name="r2">The Rectangle to evaluate.</param>
        public bool Intersects(Rectangle r2)
        {
            return !(r2.Left > Right
                     || r2.Right < Left
                     || r2.Top > Bottom
                     || r2.Bottom < Top
                    );

        }
        /// <summary>
        /// Changes the position of the Rectangle.
        /// </summary>
        /// <param name="offsetX">Change in the x-position.</param>
        /// <param name="offsetY">Change in the y-position.</param>
        public void Offset(int offsetX, int offsetY)
        {
            X += offsetX;
            Y += offsetY;
        }
        /// <summary>
        /// Changes the position of the Rectangle.
        /// </summary>
        /// <param name="offset">The values to adjust the position of the Rectangle by.</param>
        public void Offset(Point offset)
        {
            X += offset.X;
            Y += offset.Y;
        }
        /// <summary>
        /// Retrieves a string representation of the current object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("{{X:{0} Y:{1} Width:{2} Height:{3}}}", X, Y, Width, Height);
        }
        #endregion

        #region Operators
        /// <summary>
        /// Compares two rectangles for equality.
        /// </summary>
        /// <param name="a">Source rectangle.</param>
        /// <param name="b">Source rectangle.</param>
        public static bool operator ==(Rectangle a, Rectangle b)
        {
            return ((a.X == b.X) && (a.Y == b.Y) && (a.Width == b.Width) && (a.Height == b.Height));
        }
        /// <summary>
        /// Compares two rectangles for inequality.
        /// </summary>
        /// <param name="a">Source rectangle.</param>
        /// <param name="b">Source rectangle.</param>
        public static bool operator !=(Rectangle a, Rectangle b)
        {
            return !(a == b);
        }
        #endregion
    }
}